
#include <BOpenGL>
#include <BRender>
#include <BMesh>
#include "member_BMesh.h"

using namespace BWE;

#define member					(*(member_BMesh*)_ptr)
#define member_allocate()		_ptr = new member_BMesh(this)
#define member_release()		delete (member_BMesh*)_ptr

BMesh::BMesh(const BString& name)
{
	member_allocate();
	this->setName(name);
}
BMesh::BMesh(const BMesh& other) : BShape(other)
{
	member_allocate();
	this->setName(other.name());
	this->copy(other);
}
BMesh::~BMesh()
{
	member_release();
}

void BMesh::copy(const BShape& other)
{
	BShape::copy(other);
	member.vertices = mesh_member(&other)->vertices;
	member.normals = mesh_member(&other)->normals;
	member.colors = mesh_member(&other)->colors;
	member.coords = mesh_member(&other)->coords;
	member.faces = mesh_member(&other)->faces;
	member.faceOctTree = 0;
}
BShape* BMesh::clone() const
{
	BMesh* mesh = new BMesh(*this);
	return mesh;
}

void BMesh::setOptimized(bool optimized)
{
	if (optimized && member.faceOctTree.empty())
	{
		member.faceOctTree = new FaceOctTree();
		member.slotFreshFaceOctTree();
		member.connect(this, Signal_SpaceChanged, &member_BMesh::slotFreshFaceOctTree);
		return;
	}
	if (!optimized && !member.faceOctTree.empty())
	{
		member.disconnect(this, Signal_SpaceChanged);
		member.faceOctTree = 0;
		return;
	}
}
bool BMesh::optimized() const
{
	return !member.faceOctTree.empty();
}

void BMesh::setVertices(BVectorArray* vertices)
{
	if (member.vertices != vertices)
	{
		member.vertices = vertices;
	}
	this->dirty();
}
BVectorArray* BMesh::vertices()
{
	return member.vertices.ptr();
}
const BVectorArray* BMesh::vertices() const
{
	return member.vertices.ptr();
}

void BMesh::setNormals(BVectorArray* normals)
{
	if (member.normals != normals)
	{
		member.normals = normals;
	}
	emit(Signal_Fresh);
}
BVectorArray* BMesh::normals()
{
	return member.normals.ptr();
}
const BVectorArray* BMesh::normals() const
{
	return member.normals;
}

void BMesh::setFaces(BFaceArray* faces)
{
	if (member.faces != faces)
	{
		member.faces = faces;
	}
	this->fresh();
}
BFaceArray* BMesh::faces()
{
	return member.faces.ptr();
}
const BFaceArray* BMesh::faces() const
{
	return member.faces.ptr();
}

void BMesh::setColors(BColorArray* colors)
{
	if (member.colors != colors)
	{
		member.colors = colors;
	}
	this->fresh();
}
BColorArray* BMesh::colors()
{
	return member.colors;
}
const BColorArray* BMesh::colors() const
{
	return member.colors;
}

bool BMesh::setTexCoords(BCoordArray* texCoords, int unit)
{
	if (unit > 30)
		return false;
	if (member.coords.size() < unit + 1)
		member.coords.resize(unit + 1);
	member.coords[unit] = texCoords;
	for (int i = member.coords.size(); i > 0; i--)
	{
		if (member.coords[i - 1])
		{
			member.coords.remove(i, member.coords.size());
			break;
		}
	}
	emit(Signal_Fresh);
	return true;
}
BCoordArray* BMesh::texCoords(int unit)
{
	return member.coords(unit);
}
const BCoordArray* BMesh::texCoords(int unit) const
{
	return member.coords(unit);
}

bool BMesh::triangle(int index, BTriangle& triangle) const
{
	if (member.faces)
	{
		if (!member.faces->check(index))
			return false;
		const BFace& face = member.faces->at(index);
		triangle.a() = member.vertices->at(face.a());
		triangle.b() = member.vertices->at(face.b());
		triangle.c() = member.vertices->at(face.c());
		return true;
	}
	index *= 3;
	if (index >= 0 && index <= member.vertices->size() - 3)
	{
		triangle.a() = member.vertices->at(index);
		triangle.b() = member.vertices->at(index + 1);
		triangle.c() = member.vertices->at(index + 2);
		return true;
	}
	return false;
}
BTriangle BMesh::triangle(int index) const
{
	BTriangle temp;
	this->triangle(index, temp);
	return temp;
}

bool BMesh::empty() const
{
	return member.vertices == 0;
}
void BMesh::smooth()
{
	BVectorArray* vertices = member.vertices;
	BFaceArray* faces = member.faces;
	if (!vertices)
		return;
	BVectorArray* normals = member.normals;
	if (!normals || normals->size() != vertices->size())
		normals = new BVectorArray(vertices->size());
	normals->fill(BVector());
	if (faces)
	{
		for (int i = 0; i < faces->size(); i++)
		{
			BFace& face = (*faces)[i];
			const BVector& va = (*vertices)[face.a()];
			const BVector& vb = (*vertices)[face.b()];
			const BVector& vc = (*vertices)[face.c()];
			BVector normal = (vb - va).cross(vc - va);
			if (normal.normalize())
			{
				(*normals)[face.a()] += normal;
				(*normals)[face.b()] += normal;
				(*normals)[face.c()] += normal;
			}
		}
		for (int i = 0; i < normals->size(); i++)
		{
			(*normals)[i].normalize();
		}
	}
	else
	{
		for (int i = 0; i < vertices->size(); i += 3)
		{
			const BVector& va = (*vertices)[i];
			const BVector& vb = (*vertices)[i + 1];
			const BVector& vc = (*vertices)[i + 2];
			BVector normal = (vb - va).cross(vc - va);
			if (normal.normalize())
			{
				(*normals)[i] = normal;
				(*normals)[i + 1] = normal;
				(*normals)[i + 2] = normal;
			}
		}
	}
	this->setNormals(normals);
}
void BMesh::reverse()
{
	if (member.vertices.empty())
		return;

	if (BFaceArray* faces = member.faces)
	{
		int size = member.faces->size();
		for (int i = 0; i < size; i++)
		{
			BFace& face = (*faces)[i];
			bSwap(face.b(), face.c());
		}
	}
	else
	{
		int size = member.vertices->size();
		for (int i = 0; i < size; i += 3)
		{
			BVector& vb = (*member.vertices)[i + 1];
			BVector& vc = (*member.vertices)[i + 2];
			bSwap(vb, vc);
		}
	}
	if (BVectorArray* normals = member.normals)
	{
		for (int i = 0; i < normals->size(); i++)
		{
			(*normals)[i] = -(*normals)[i];
		}
	}
	this->dirty();
}

void BMesh::clear()
{
	member.vertices = 0;
	member.normals = 0;
	member.faces = 0;
	member.colors = 0;
	member.coords.clear();
	this->dirty();
}

void BMesh::refresh(BBox& box)
{
	BSpace space;
	if (member.vertices && member.vertices->size())
	{
		space.reset(member.vertices->first());
		for (int i = 0; i < member.vertices->size(); i++)
		{
			space.expand((*member.vertices.ptr())[i]);
		}
	}
	box = space;
}
void BMesh::render(BRender& render)
{
	if(!member.vertices || member.vertices->empty())
		return;

	if (member.vertices)
	{
		glEnableClientState(GL_VERTEX_ARRAY);
		BOpenGL::glVertexArray(member.vertices);
	}

	if (member.normals)
	{
		glEnableClientState(GL_NORMAL_ARRAY);
		BOpenGL::glNormalArray(member.normals);
	}

	if (member.colors)
	{
		glEnableClientState(GL_COLOR_ARRAY);
		glColorPointer(4, GL_UNSIGNED_BYTE, 0, member.colors->data());
	}

	for (int i = 0; i < member.coords.size(); i++)
	{
		if (member.coords[i] && this->texture(i))
		{
			glClientActiveTexture(GL_TEXTURE0 + i);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			glTexCoordPointer(2, GL_FLOAT, 0, member.coords[i]->data());
		}
	}

	if (member.faces)
	{
		BFaceArray& facesRef = *member.faces;
		glEnableClientState(GL_INDEX_ARRAY);
		glDrawElements(GL_TRIANGLES, member.faces->size() * 3, GL_UNSIGNED_INT, member.faces->data());
		glDisableClientState(GL_INDEX_ARRAY);
	}
	else
	{
		glDrawArrays(GL_TRIANGLES, 0, member.vertices->size());
	}

	if (member.vertices)
	{
		glDisableClientState(GL_VERTEX_ARRAY);
	}
	if (member.normals)
	{
		glDisableClientState(GL_NORMAL_ARRAY);
	}
	if (member.colors)
	{
		glDisableClientState(GL_COLOR_ARRAY);
	}
	for (int i = 0; i < member.coords.size(); i++)
	{
		if (member.coords[i] && this->texture(i))
		{
			glClientActiveTexture(GL_TEXTURE0 + i);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		}
	}
}
